package com.millstein.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.millstein.enums.BizCodeEnum;
import com.millstein.enums.product.LockStateEnum;
import com.millstein.exception.BizException;
import com.millstein.mapper.ProductTaskMapper;
import com.millstein.model.BannerDO;
import com.millstein.model.ProductDO;
import com.millstein.mapper.ProductMapper;
import com.millstein.model.ProductMessage;
import com.millstein.model.ProductTaskDO;
import com.millstein.properties.RabbitMQProperties;
import com.millstein.request.LockProductItemRequest;
import com.millstein.request.LockProductRequest;
import com.millstein.service.ProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.millstein.service.ProductTaskService;
import com.millstein.vo.BannerVO;
import com.millstein.vo.ProductVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author millstein
 * @since 2021-09-16
 */
@Service
@Slf4j
@EnableConfigurationProperties(RabbitMQProperties.class)
public class ProductServiceImpl extends ServiceImpl<ProductMapper, ProductDO> implements ProductService {

    @Autowired
    private ProductTaskMapper productTaskMapper;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQProperties rabbitMQProperties;

    /**
     * 分页查询商品信息
     *
     * @param page 当前页
     * @param size 每页记录数
     * @return
     */
    @Override
    public Page<ProductVO> pageProduct(int page, int size) {
        Page<ProductDO> doPage = new Page<>(page, size);
        QueryWrapper<ProductDO> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("create_time");
        this.page(doPage, wrapper);

        List<ProductVO> voList = doPage.getRecords().stream().map(ProductVO::new).collect(Collectors.toList());
        Page<ProductVO> voPage = new Page<>();
        BeanUtils.copyProperties(doPage, voPage);
        voPage.setRecords(voList);
        return voPage;
    }

    /**
     * 根据id查询商品信息
     *
     * @param id 商品id
     * @return
     */
    @Override
    public ProductVO findDetail(Long id) {
        ProductDO productDO = this.getById(id);
        return productDO == null ? null : new ProductVO(productDO);
    }

    /**
     * 锁定商品的库存
     *
     * @param lockProductRequest 锁定商品库存信息
     */
    @Override
    public void lockProduct(LockProductRequest lockProductRequest) {
        // 1.修改商品表中的锁定数量
        updateLockStock(lockProductRequest);

        // 2.在商品任务表中添加锁定记录
        List<ProductTaskDO> list = saveProductTask(lockProductRequest);

        // 3.投递延时消息
        sendDelayMessageForReleaseLock(list, lockProductRequest.getOutTradeOrderNo());
    }

    /**
     * 修改商品表中的锁定数量
     * @param lockProductRequest 锁定商品库存信息
     */
    private void updateLockStock(LockProductRequest lockProductRequest) {
        List<LockProductItemRequest> products = lockProductRequest.getProducts();
        if (CollectionUtil.isEmpty(products)) {
            throw new BizException(BizCodeEnum.RETURN_DATA_IS_NONE);
        }

        int rows = productMapper.updateBatchLockState(products);

        if (rows == 0) {
            throw new BizException(BizCodeEnum.PRODUCT_LOCK_FAILURE);
        }
    }

    /**
     * 在商品任务表中添加锁定记录
     * @param lockProductRequest 锁定商品库存信息
     * @return
     */
    private List<ProductTaskDO> saveProductTask(LockProductRequest lockProductRequest) {
        List<Long> productIds = lockProductRequest.getProducts().stream()
                .map(LockProductItemRequest::getProductId).collect(Collectors.toList());

        // 为了填充下面的productName字段，这里要批量对商品信息进行查询
        List<ProductDO> productDOS = this.listByIds(productIds);
        Map<Long, ProductDO> productMap = productDOS.stream()
                .collect(Collectors.toMap(ProductDO::getId, Function.identity()));

        List<ProductTaskDO> list = lockProductRequest.getProducts().stream().map(item -> {
            ProductTaskDO productTaskDO = new ProductTaskDO();
            productTaskDO.setProductId(item.getProductId());
            productTaskDO.setBuyNum(item.getBuyNum());
            productTaskDO.setCreateTime(new Date());
            productTaskDO.setOutTradeNo(lockProductRequest.getOutTradeOrderNo());
            productTaskDO.setProductName(productMap.get(item.getProductId()) == null ? null :
                    productMap.get(item.getProductId()).getTitle());
            productTaskDO.setLockState(LockStateEnum.LOCK.name());
            return productTaskDO;
        }).collect(Collectors.toList());

        int rows = productTaskMapper.saveBatchTask(list);

        if (rows != list.size()) {
            throw new BizException(BizCodeEnum.PRODUCT_LOCK_FAILURE);
        }

        return list;
    }

    /**
     * 发送延时消息，这个消息是当订单没有成功支付时，用来解锁库存的
     * @param list 库存锁定记录
     * @param outTradeOrderNo 订单编号
     */
    private void sendDelayMessageForReleaseLock(List<ProductTaskDO> list, String outTradeOrderNo) {
        for (ProductTaskDO productTaskDO : list) {
            ProductMessage productMessage = new ProductMessage();
            productMessage.setOutTradeNo(outTradeOrderNo);
            productMessage.setTaskId(productTaskDO.getId());
            rabbitTemplate.convertAndSend(rabbitMQProperties.getProductEventExchange(),
                    rabbitMQProperties.getProductReleaseDelayRoutingKey(), productMessage);
            log.info("库存锁定消息发送成功：{}", productMessage);
        }
    }
}
